<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of compilador
 *
 * @author 
 */
class compila {
    /*
     * Posibles Tokens:
     *  variable,
     */

    private $cuentaChar = 0;        //controla el numero de comillas simples
    private $sumCorchete = 0;       //Suma el numero de corchetes
    private $sumParRedon = 0;       //Suma el numero de parentesis redondos
    private $sumParCuadra = 0;       //Suma el numero de parentesis cuadrados
    private $escape = 0;        //controla el numero de caracteres de escape
    private $compilado = ""; //contiene todas las frases y palabras que ya se han analisado
    public $compiladoPrograma = "";    //Contiene todo el programa que se ha compilado
    public $error = false; //almacen todos los errores econtrados al momento de crear los tokens
    private $tipoErrores = ""; //Almacena toda la lista de errores cometidos
    private $palabra = ""; //almacen la palabra que se esta construyendo caracter por caracter
    public $posToken = ""; //almacena el posible token de la palabra
    public $programa = "";         //contiene todos los tokens del programa
    private $tokens = "";           //almacena la lista de tokens por frase
    private $con;           //conexion de la base de datos
    private $i = 0;             //indica si aumenta el contador de la cadena
    private $isFor = false;     //indica si esta en un ciclo for
    private $erorr_ = false;    //indica posible error
    private $parFor = 0;    //cuenta los parentesisi para el ciclo for
    private $terminaFor = false; //indica que ha termina el ciclo for
    public $transaccionError = "";    //Transaccion para los errores
    private $admiteE = true;    //bandera que indica si admite la E en los numeros
    private $tokenAnterior = ""; //almacena los tokens anteriores
    private $signo = "";        //Signo para los valores enteros
    public function nuevoCaracter($char, $sig) {
        //Inicializa
        $this->i = 0;
        // fin Inicializa
        $this->identifica($char, $sig);
        return $this->i;
    }

    private function identifica($char, $sig) {
        /* Funcion que identifica que token es la palabra */
        $ascii = ord($char);
        //if($sig=="") $this->isUltimo = true;
        if ($ascii == 32 && $this->posToken != "string" && $this->posToken != "char") { //si es un espacio limpia el analisis
            $this->limpia();
            return;
        }
        if ($this->isString($char, $ascii))
            return true; //Si es un char o string
        if (!$this->operador($char, $sig)) { //no es un operador
            if (($this->posToken == "entero" || $this->posToken == "decimal") && $this->admiteE && ($ascii == 101 || $ascii == 69))
                if ($this->isEntero($char, $ascii,$sig)) {
                    return true; //es un numero y se toma como un entero
                }
            if ($this->isVariable($char, $ascii))
                return true; //es una variable
            if ($this->isEntero($char, $ascii))
                return true; //es un numero y se toma como un entero
            if ($ascii == 44) { //ES UNA COMA
                $this->limpia();
                $this->palabra = ",";
                $this->posToken = "coma";
                $this->limpia();
                return true;
            }
            $this->publicaError("que es?", __METHOD__, $char);
            return;
        }else
            return;
    }

//fin private function identifica($char, $sig) {

    private function isEntero($char, $ascii, $opc = null) {
        if ($ascii >= 48 && $ascii <= 57) { //es un número 0 - 9 
            if($this->posToken == ""){
                $this->palabra = trim($this->palabra.$this->signo);
                $this->signo = "";
            }
            $this->posToken = ($this->posToken == "") ? "entero" : $this->posToken;
            $this->palabra = trim($this->palabra . $char);
            return true;
        } elseif ($ascii == 46) { //se trata del separador de miles
            $tmp = strpos($this->palabra, ".");
            if ($tmp !== false) { //si lo encontro es un error
                $this->publicaError("no Decimal", __METHOD__);
                return true;
            }
            switch ($this->posToken) {
                case 'entero':
                    $this->palabra = trim($this->palabra . $char);
                    $this->posToken = "decimal";
                    return true;
                    break;
                case 'variable':
                    if ($ascii == 46) {
                        $this->publicaError("no Variable", __METHOD__);
                        return true;
                    }
                    break;
            }
        }elseif($ascii == 101 || $ascii == 69){ //se trata de la letra e
            if(!$this->admiteE){
                $this->publicaError("exceso E", __METHOD__);
                return true;
            }else{
                    $this->palabra = trim($this->palabra . $char);
                    $this->posToken = "decimal";
                    if($opc == '+' || $opc == '-'){
                        $this->palabra = trim($this->palabra . $opc);
                        $this->i ++;
                    }
                    $this->admiteE = false;
                    return true;
            }
        }
        return false;
    }

//fin private function isEntero($char, $ascii,$opc = null) {

    private function isString($char, $ascii) {
        /* Funcion para identificar si se trata de un char o cadena */
        //los caracteres de comillas solo se permiten si no existe nada evaluado anteriormente
        if ($this->posToken == "") {
            switch ($ascii) {
                case 39: //es una char
                    $this->cuentaChar += 1;
                    $this->palabra = $this->palabra . $char;
                    $this->posToken = "char";
                    return true;
                    break;
                case 34: //es una cadena
                    $this->palabra = $this->palabra . $char;
                    $this->cuentaChar = 1;
                    $this->posToken = "string";
                    return true;
                    break;
            }
            return false;
        } else {
            switch ($this->posToken) {
                case "char":
                    if ($ascii == 39) {
                        if ($this->cuentaChar == 2) { //se trata de un char 39= comilla simple
                            $this->cuentaChar = 0;
                            $this->palabra = $this->palabra . $char;
                            $this->limpia();
                        } else {
                            switch ($this->cuentaChar) {
                                case '1':
                                    $this->palabra = $this->palabra . $char;
                                    $this->cuentaChar += 1;
                                    break;
                                default:
                                    $this->publicaError("error en Char", __METHOD__);
                                    break;
                            }
                        }
                        return true;
                    }
                    switch ($this->cuentaChar) {
                        case '1':
                            $this->palabra = $this->palabra . $char;
                            $this->cuentaChar += 1;
                            break;

                        default:
                            $this->publicaError("error en Char", __METHOD__);
                            break;
                    }
                    return true;
                    break;
                case "string":
                    if ($ascii == 34) {
                        if ($this->escape == 1) { //se trata de imprimir la comilla en su caracter de escape
                            $this->palabra = $this->palabra . $char;
                            $this->escape = 0;
                        } else {
                            $this->palabra = $this->palabra . $char;
                            $this->cuentaChar = 0;
                            $this->limpia();
                        }
                        return true;
                    }
                    if ($ascii == 64) { //se tratra de la barra de escape
                        $this->escape = 1;
                    }
                    $this->palabra = $this->palabra . $char;
                    if ($this->escape > 0)
                        $this->escape--;
                    return true;
                    break;
            }
        }
    }

//fin private function isString($char,$ascii){

    public function publicaError($tipo = null, $lugar = null, $opc=null) {
        $this->erorr_ = true;
        $this->tipoErrores = $tipo;
        if (!isset($opc))
            $this->fraseCompila();
        return false;
    }

    private function isVariable($char, $ascii) {
        /* funcion para identificar si es una variable, de serlo realiza sus calculos y retorna un true */
        if (($ascii >= 65 && $ascii <= 90) || ($ascii >= 97 && $ascii <= 122) || $ascii == 95) {
            if ($this->posToken != "variable" && $this->posToken != "" && $this->posToken != "do") {
                $this->publicaError("no Variable", __METHOD__);
                return true;
            }
            $this->palabra = trim($this->palabra . $char);
            $tmp = mysql_real_escape_string($this->palabra);
            $sql = "select * from comandosc where comando = '$tmp' COLLATE utf8_bin";
            $result = $this->con->consulta($sql);
            if ($this->con->numeroFilas($result) > 0) {
                if ($tmp == "for")
                    $this->isFor = true;
                $result = $this->con->unaFila($result);
                $this->posToken = $result["comandoSignificado"];
            } else { //puede ser una variable
                $this->posToken = "variable";
            }
            return true;
        }else
            return false;
    }

    private function operador($valor, $sig) {
        //valida si es un operador
        $tmp = mysql_real_escape_string($valor);
        $sql = "select * from comandosc where comando like '%$tmp%' and comandoSignificado like 'opera%' COLLATE utf8_bin";
        $comando = $this->con->consulta($sql);
        $tmp = $this->con->numeroFilas($comando);
        if ($tmp > 0) {
            $operador = mysql_real_escape_string(trim($valor . $sig));
            //Busca si es un operador compuesto
            $sql = "select * from comandosc where comando = '$operador' and comandoSignificado like 'opera%' COLLATE utf8_bin";
            $result = $this->con->consulta($sql);
            if ($this->con->numeroFilas($result) > 0) {
                $comando = $this->con->unaFila($result);
                $this->i++;
            } else {
                $operador = $valor;
                $sql = "select * from comandosc where comando = '$valor' and comandoSignificado like 'opera%' COLLATE utf8_bin";
                $result = $this->con->consulta($sql);
                if ($this->con->numeroFilas($result) > 0) {
                    $comando = $this->con->unaFila($result);
                }else
                    return false;
            }
            if ($this->sumParRedon < 0 || $this->sumCorchete < 0 || $this->sumParCuadra < 0) {
                $this->publicaError("parentesis invertido", __METHOD__);
            }

            switch ($comando["comandoSignificado"]) {
                case 'operaAgrupaParAbre':
                    if ($this->isFor && $this->parFor == 0)
                        $this->parFor++;
                    else
                        $this->sumParRedon++;
                    break;
                case 'operaAgrupaParCie':
                    if ($this->isFor && $this->sumParRedon == 0 && $this->parFor == 1) {
                        $this->parFor--;
                        $this->terminaFor = true;
                    }else
                        $this->sumParRedon--;
                    break;
                case 'operaAgrupaCorAbre':
                    $this->sumCorchete++;
                    break;
                case 'operaAgrupaCorCie':
                    $this->sumCorchete--;
                    break;
                case 'operaAgrupaCuadraAbre':
                    $this->sumParCuadra++;
                    break;
                case 'operaAgrupaCuadraCie':
                    $this->sumParCuadra--;
                    break;
                case 'operaAritmetico':
                    //exit("mama".$this->tokenAnterior." -- ".$valor);
                    if($valor == '-' && $this->tokenAnterior == "operaAsignacion"){
                        $this->signo = '-';
                        return true;
                    }
                    break;
            }
            $this->limpia();
            $this->palabra = $operador;
            $this->posToken = $comando["comandoSignificado"];
            $this->limpia();
            return true;
        }else
            return false;
    }

//fin private function operador($valor){

    private function sumaToken($opc, $token) {
        $tmp = "";
        /* Funcion para incrementar la lista de tokens */
        switch ($opc) {
            case 'l': //se trata de un incremento para volver a iniciar el analisis
                $tmp = ($this->posToken != "") ? "$" . "{$this->posToken}" : "";
                $tmp .= ($token != "") ? "$-- $token" : "";
                $this->tokens = $this->tokens . $tmp;
                $this->tokenAnterior = trim($tmp,"$");
                break;

            default:
                break;
        }
        return true;
    }

    public function mostrar() {
        if ($this->sumCorchete > 0) {
            $this->publicaError("cierre corchete", __METHOD__, 'no');
        }
        if ($this->sumCorchete < 0) {
            $this->publicaError("exceso corchete", __METHOD__, 'no');
        }
        if ($this->error) {
            $sql = "select errorMensaje from errores where errorCod = '{$this->tipoErrores}'";
            $result = $this->con->consulta($sql);
            $result = $this->con->unaFila($result);
            //Si existe el mismo tipo de error lo elimina de la tabla temporal
            $sql = "delete from temporal where transaccion = '{$this->transaccionError}' and tempIndVar1 = '{$this->tipoErrores}'";
            $this->con->consulta($sql);
            //Inserta el error en la temporal con la transaccion principal
            $sql = "insert into temporal (transaccion,tempIndVar1,tempIndVar2) values(";
            $sql .= "'{$this->transaccionError}','{$this->tipoErrores}','errores')";
            $this->con->consulta($sql);
            $this->compiladoPrograma = $this->compiladoPrograma . "$" . $this->compilado;
            $mensaje = "<div id='divMensajesCompila' class='tituloErrorCompila'>ERROR: {$result["errorMensaje"]}</div>";
            $tmp = str_replace("$", " ", $this->compiladoPrograma);
            $mensaje .= str_replace(";", ";<br/> ", $tmp);
            ?>
            <script type="text/javascript">
                $("#erroresCompilacion").val('0');
                if(enviaCompilacion)
                    fnEnviaRespuestas();
            </script>
            <?php
            return true;
        } else {
            return false;
        }
    }

    private function validaciones() {
        /* Funcion para realizar las validaciones por cada token
         * retorna false si no hay problema de lo contrario true
         */
        switch ($this->posToken) {
            case 'char':
                if ($this->cuentaChar > 0) {
                    $this->publicaError("error en Char", __METHOD__);
                    return true;
                }
                break;
            case 'string':
                if ($this->escape > 0) {
                    $this->publicaError("error escape", __METHOD__);
                    return true;
                }
                if ($this->cuentaChar > 0) {
                    $this->publicaError("error en string", __METHOD__);
                    return true;
                }
                break;
        }
        if ($this->isFor && $this->terminaFor) {
            if ($this->parFor != 0) {
                $this->publicaError("mal for", __METHOD__);
                return true;
            }
            $this->isFor = FALSE;
            $this->terminaFor = FALSE;
        }
        return false;
    }

    public function limpia($termina = null, $token = null) {
        //realiza las validaciones del caso

        if ($this->validaciones())
            return true;
        if ($this->posToken != "" || $token != "") {
            $this->sumaToken('l', $token);
            $this->fraseCompila();
            $this->palabra = "";
            $this->posToken = "";
        }
        if ($termina == ';' && $this->tokens != "") {
            //$this->tokens = $this->tokens . $termina;
            $tmp = "";
            $tmp1 = "";
            if ($this->sumParRedon > 0){
//                echo "hola";
                $this->publicaError("cierre parentesis", __METHOD__, 'no');
            }if ($this->sumParRedon < 0)
                $this->publicaError("exceso parentesis", __METHOD__, 'no');
            if ($this->sumParCuadra > 0)
                $this->publicaError("cierre cuadrado", __METHOD__, 'no');
            if ($this->sumParCuadra < 0)
                $this->publicaError("exceso cuadrado", __METHOD__, 'no');
            if ($this->error) {
                $tmp = "<span class='errorCompila'>";
                $tmp1 = "</span>";
            }
            $this->programa = $this->programa . "$" . $tmp . trim($this->tokens, "$") . $tmp1 . $termina;
            $this->compiladoPrograma = $this->compiladoPrograma . "$" . $tmp . trim($this->compilado, "$") . $tmp1 . $termina;
            $this->compilado = "";
            $this->tokens = "";
            $this->admiteE = true;
        }
        return;
    }

    public function conexion($con) {
        /* Establece la conexion con el servidor */
        $this->con = $con;
    }

    public function fraseCompila() {
        if ($this->error)
            $this->compilado = $this->compilado . "$" . "<span class='errorCompila'>" . $this->palabra . "</span>";
        else
            $this->compilado = $this->compilado . "$" . $this->palabra;
        return;
    }

}
?>
